﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using YamrSyncDemo.DBSync;
using YamrSyncDemo.YammerData;

namespace YamrSyncDemo.YammerWrapper
{
    public class YammerConnection
    {
        public bool IsConnected { get { return !string.IsNullOrEmpty(AccessToken); } }
        
        private YammerConnectionConfig config;
        private string sessionCookie = string.Empty;
        private string yamtrackCookie = string.Empty;
        private CookieContainer cc = new CookieContainer(2);
        private HttpWebResponse wResp;
        private HttpWebRequest wr;
        public string AccessToken { get; set; }

        public YammerConnection(YammerConnectionConfig config)
        {
            this.config = config;
        }

        public bool Connect()
        {
            if (IsConnected)
                return true;

            string qsCode = string.Empty;
            string postResults = string.Empty;
            string response = string.Empty;

            while (string.IsNullOrEmpty(qsCode))
            {
                response = MakeGetRequest(config.PermUrl);

                //look for authenticity token
                string authToken = GetAuthenticityToken(response);

                if (!string.IsNullOrEmpty(authToken))
                {
                    //POST /session?client_id=zdpOzrUurBtvHhoTJ00wQ HTTP/1.1

                    //sample post body:
                    //utf8=%E2%9C%93&authenticity_token=bP%2Bh0zNGkeL7z%2Bj5stJ%2FPsyZb1O75SPmS5RpkXfSFgo%3D&network_permalink=&login=speschka@contoso.com&password=asdfadsf&remember_me=on
                    //string foo = System.Web.HttpUtility.UrlEncodeUnicode("bP+h0zNGkeL7z+j5stJ/PsyZb1O75SPmS5RpkXfSFgo="); //.net recommends UrlEncode

                    //*****************************************************
                    //need to abstract the post calls to a generic function
                    //*****************************************************
                    string postBody = "utf8=%E2%9C%93&authenticity_token=" + System.Web.HttpUtility.UrlEncode(authToken) + "&network_permalink=&login=" +
                        config.UserName + "&password=" + config.Password + "&remember_me=on";

                    //make the first post for code
                    postResults = MakePostRequest(postBody, config.AuthUrl1);

                    //get the next auth token that was returned and can be used to get an access code
                    string preCodeToken = GetAuthenticityToken(postResults);

                    //********************************************************************
                    //REMOVED 12/17/2013 TO REFLECT CHANGES YAMMER MADE IN AUTH PROCEDURE
                    ////POST /contoso.com/oauth2/adsfasdfsadf/authorize?client_id=asdfasdfasdf&display=page&redirect_uri=http%3A%2F%2Fsppdxwin8&response_type=code HTTP/1.1
                    ////sample post body:
                    ////authenticity_token=KBBgLyjZYGKzUs616hEV5BGqgX6N4D0ejJZ%2FG4zvDsI%3D&_method=post

                    //utf8=%E2%9C%93&authenticity_token=sefnlza37pC0aPaYF7pvCtjGg54APtqf8wyvB6NHX5Q%3D&allow=Allow
                    postBody = "utf8=%E2%9C%93&authenticity_token=" + System.Web.HttpUtility.UrlEncode(preCodeToken) + "&allow=Allow";

                    //////make the next post
                    postResults = MakePostRequest(postBody, config.AuthUrl2, null, null, false);
                    //********************************************************************

                    //******************************************************************
                    //ADDED 12/17/2013 TO REFLECT CHANGES YAMMER MADE IN AUTH PROCEDURE
                    //response = MakeGetRequest(permUrl, string.Empty, true);
                    //******************************************************************
                }

                //now look for the query string
                //qsCode = wResp.ResponseUri.Query;
                qsCode = wResp.Headers["Location"].Split(new[] { '?' })[1];
                wResp.Close();
            }

            if (qsCode.IndexOf("code") > -1)
            {
                string accessCode = qsCode.Substring(qsCode.IndexOf("=") + 1);

                response = MakeGetRequest(config.AccTokenUrl + accessCode);

                if (!string.IsNullOrEmpty(response))
                {
                    YammerAccessToken jat = YammerAccessToken.GetInstanceFromJson(response);

                    if (!string.IsNullOrEmpty(jat.TokenResponse.Token))
                    {
                        AccessToken = jat.TokenResponse.Token;
                        return true;
                    }
                }
            }

            return false;
        }

        public List<T> GetPagedData<T>(string Url, string accessToken)
        {
            List<T> results = new List<T>();

            try
            {
                //set up paging
                int curPage = 0;
                string response = "start";

                string qsOperator = (Url.IndexOf("?") > -1) ? "&" : "?";

                while (response != "[]")
                {
                    //NOTE:  Yammer only allows 10 requests in 10 seconds for this
                    //type of data so you need to put a one second sleep in between
                    //each request or you will get a 429 response code back 
                    //(too many requests)
                    System.Threading.Thread.Sleep(1000);
                    curPage += 1;
                    response = MakeGetRequest(Url + qsOperator + "page=" + curPage.ToString(), accessToken);
                    List<T> resultSet = JsonConvert.DeserializeObject<List<T>>(response);

                    //if (typeof(T) == typeof(YammerGroup))
                    //{
                    //    List<YammerGroup> filteredList = resultSet.OfType<YammerGroup>().Where(p => p.PrivacyLevel == "public").ToList();
                    //    results.AddRange(filteredList.OfType<T>().AsEnumerable());
                    //}
                    //else
                    results.AddRange(resultSet);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error getting paged data: " + ex.Message);
            }

            return results;
        }

        public YammerGroupMembers GetGroupMembersPaged(long groupID, string Url, string accessToken)
        {
            var result = new YammerGroupMembers();
            result.GroupID = groupID;

            try
            {
                //set up paging
                int curPage = 0;
                string response = "start";

                string qsOperator = (Url.IndexOf("?") > -1) ? "&" : "?";
                YammerGroupMembers resultMembers = null;

                do
                {
                    //NOTE:  Yammer only allows 10 requests in 10 seconds for this
                    //type of data so you need to put a one second sleep in between
                    //each request or you will get a 429 response code back 
                    //(too many requests)
                    System.Threading.Thread.Sleep(1000);
                    curPage += 1;
                    response = MakeGetRequest(Url + qsOperator + "page=" + curPage.ToString(), accessToken);
                    resultMembers = JsonConvert.DeserializeObject<YammerGroupMembers>(response);

                    result.Users.AddRange(resultMembers.Users);
                } while (resultMembers.MoreAvailable);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error getting paged data: " + ex.Message);
            }

            return result;
        }

        public List<YammerMessage> GetMessagesPaged(string Url, string accessToken)
        {
            var result = new List<YammerMessage>();

            try
            {
                //set up paging
                string response = "start";

                string qsOperator = (Url.IndexOf("?") > -1) ? "&" : "?";
                YammerMessages messages = null;
                long olderThan = -1;

                do
                {
                    //NOTE:  Yammer only allows 10 requests in 10 seconds for this
                    //type of data so you need to put a one second sleep in between
                    //each request or you will get a 429 response code back 
                    //(too many requests)
                    System.Threading.Thread.Sleep(1000);
                    if (olderThan == -1)
                        response = MakeGetRequest(Url, accessToken);
                    else
                        response = MakeGetRequest(Url + qsOperator + "older_than=" + olderThan.ToString(), accessToken);
                    messages = JsonConvert.DeserializeObject<YammerMessages>(response);

                    result.AddRange(messages.Messages);
                    if (messages.Messages.Count > 0)
                    {
                        olderThan = messages.Messages[messages.Messages.Count - 1].ID;
                    }
                } while (messages.Messages.Count > 0);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error getting paged data: " + ex.Message);
            }

            return result;
        }

        private static string GetAuthenticityToken(string rawHtml)
        {
            string result = string.Empty;

            try
            {
                int at = rawHtml.IndexOf("<meta name=\"authenticity_token\" id=\"authenticity_token\"");

                if (at > -1)
                {
                    //get the authenticity token string
                    int et = rawHtml.IndexOf("/>", at + 1);
                    string tokenText = rawHtml.Substring(at, et - at);

                    //get the token value
                    int ts = tokenText.IndexOf("content=");
                    int es = tokenText.LastIndexOf("\"");

                    result = tokenText.Substring(ts + 9, es - ts - 9);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in GetAuthenticityToken: " + ex.Message);
            }

            return result;
        }
        
        private void SetCookies()
        {
            try
            {
                const string YAMTRAK_COOKIE = "yamtrak_id";
                const string SESSION_COOKIE = "_workfeed_session_id";

                //no normalization to Set-Cookie content and Cookies on WebRequest is not populated so 
                //we are doing guesstimating parsing
                //Set-Cookie: yamtrak_id=2f1621f7-7452-4f7e-a974-6a85eb5ca22d; path=/; expires=Fri, 26-Sep-2014 15:20:54 GMT; secure; HttpOnly,_workfeed_session_id=34a53fdeab7da22fc4ae088fb19a2307; path=/; secure; HttpOnly
                string cookies = wResp.Headers["Set-Cookie"];

                if (string.IsNullOrEmpty(cookies))
                {
                    cc = new CookieContainer();
                }
                else
                {
                    int cStart = cookies.IndexOf("=");
                    int cEnd = cookies.IndexOf("HttpOnly,");

                    //sometimes the cookie ends with "HttpOnly," and sometimes it ends with "secure"
                    if (
                        (cookies.Substring(cStart + 1, cEnd + 8 - cStart - 1).IndexOf(YAMTRAK_COOKIE) > -1) ||
                         (cookies.Substring(cStart + 1, cEnd + 8 - cStart - 1).IndexOf(SESSION_COOKIE) > -1)
                        )
                    {
                        //change the end to look for secure
                        cEnd = cookies.IndexOf("secure,");
                    }

                    string tempCook1 = cookies.Substring(cStart + 1, cEnd + 8 - cStart - 1);
                    tempCook1 = tempCook1.Remove(tempCook1.IndexOf(";"));

                    cStart = cookies.IndexOf("=", cEnd);
                    string tempCook2 = cookies.Substring(cStart + 1);
                    tempCook2 = tempCook2.Remove(tempCook2.IndexOf(";"));

                    if (cookies.StartsWith("yamtrak"))
                    {
                        yamtrackCookie = tempCook1;
                        sessionCookie = tempCook2;
                    }
                    else
                    {
                        sessionCookie = tempCook1;
                        yamtrackCookie = tempCook2;
                    }

                    //yamtrackCookie = wResp.Cookies[YAMTRAK_COOKIE].Value;
                    //sessionCookie = wResp.Cookies[SESSION_COOKIE].Value;

                    cc = new CookieContainer();
                    cc.Add(new Cookie(YAMTRAK_COOKIE, yamtrackCookie, "/", "www.yammer.com"));
                    cc.Add(new Cookie(SESSION_COOKIE, sessionCookie, "/", "www.yammer.com"));
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in SetCookies: " + ex.Message);
            }
        }

        public string MakePostRequest(string postBody, string url, string authHeader = null, string contentType = null, bool autoRedirect = true)
        {
            string results = string.Empty;

            try
            {
                //get the session and yamtrack cookie
                SetCookies();

                wr = (HttpWebRequest)WebRequest.Create(url);
                wr.Method = "POST";
                wr.CookieContainer = cc;

                //if an authHeader was provided, add it as a Bearer token to the request
                if (!string.IsNullOrEmpty(authHeader))
                    wr.Headers.Add("Authorization", "Bearer " + authHeader);

                byte[] postByte = Encoding.UTF8.GetBytes(postBody);

                if (string.IsNullOrEmpty(contentType))
                    wr.ContentType = "application/x-www-form-urlencoded";
                else
                    wr.ContentType = contentType;

                wr.AllowAutoRedirect = autoRedirect;
                wr.ContentLength = postByte.Length;
                Stream postStream = wr.GetRequestStream();
                postStream.Write(postByte, 0, postByte.Length);
                postStream.Close();

                wResp = (HttpWebResponse)wr.GetResponse();
                postStream = wResp.GetResponseStream();
                StreamReader postReader = new StreamReader(postStream);

                results = postReader.ReadToEnd();

                postReader.Close();
                postStream.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in MakePostRequest: " + ex.Message);
            }

            return results;
        }

        public string MakeGetRequest(string Url, string authHeader = null, bool AddCookies = false)
        {
            string results = string.Empty;

            try
            {
                wr = (HttpWebRequest)WebRequest.Create(Url);
                wr.Method = "GET";

                //******************************************************************
                //ADDED 12/17/2013 TO REFLECT CHANGES YAMMER MADE IN AUTH PROCEDURE
                if (AddCookies)
                {
                    SetCookies();
                    wr.CookieContainer = cc;
                }
                //******************************************************************                           

                if (!string.IsNullOrEmpty(authHeader))
                    wr.Headers.Add("Authorization", "Bearer " + authHeader);

                wResp = (HttpWebResponse)wr.GetResponse();

                Stream dataStream = wResp.GetResponseStream();
                StreamReader reader = new StreamReader(dataStream);

                results = reader.ReadToEnd();

                reader.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in MakeGetRequest: " + ex.Message);
            }

            return results;
        }

        public HttpWebResponse MakeGetRequest2(string Url, string authHeader = null, bool AddCookies = false)
        {
            string results = string.Empty;

            try
            {
                wr = (HttpWebRequest)WebRequest.Create(Url);
                wr.Method = "GET";

                //******************************************************************
                //ADDED 12/17/2013 TO REFLECT CHANGES YAMMER MADE IN AUTH PROCEDURE
                if (AddCookies)
                {
                    SetCookies();
                    wr.CookieContainer = cc;
                }
                //******************************************************************                           

                if (!string.IsNullOrEmpty(authHeader))
                    wr.Headers.Add("Authorization", "Bearer " + authHeader);

                wResp = (HttpWebResponse)wr.GetResponse();
                return wResp;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error in MakeGetRequest: " + ex.Message);
            }

            return null;
        }
    }
}
